package com.haojin.spring.aop.annoaop;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @author .29.
 * @create 2023-02-04 22:47
 */
//切面类
//@Aspect代表这是一个切面类
//@Component代表将此类交给Spring处理，能够放入IOC容器中去
@Aspect
@Component
public class LogAspect {
    //设置切入点 和 通知类型
    //切入点表达式：execution(权限修饰符 增强方法返回类型 增强方法所在类全路径.方法名称(方法参数))

    //重用切入点表达式：使用@Pointcut注解，设置需要重复使用的切入点表达式
    @Pointcut(value = "execution(public int com.haojin.spring.aop.annoaop.CalculatorImpl.*(..))")
    public void pointCut(){}
    //之后，在使用通知时，将切入点表达式用 方法名 替换，即pointCut()（同一个切面）
    //若在不同的切面，需要加上重用切入点表达式方法的全类名：com.haojin.spring.aop.annoaop.LogAspect.pointCut()（不同的切面）

    //通知类型：
    // @Before("切入点表达式配置切入点") 前置
    @Before(value = "pointCut()")   //重用切入点表达式，使用方法名替换
    public void beforeMethod(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();//获取增强方法的名字
        Object[] args = joinPoint.getArgs();             //获取增强方法的参数数组
        System.out.println("Logger-->前置通知，增强方法为："+name+",参数："+ Arrays.toString(args));
    }

    // @AfterReturning() 返回
    //返回通知 可以获取到返回值，在切入表达式中增加返回值属性:returning = ""
    @AfterReturning(value = "pointCut()",returning = "result")
    //增强方法中需要添加 返回值参数result，参数名与切入点表达式保持一致（result）
    public void afterReturningMethod(JoinPoint joinPoint,Object result){//可以存在参数JoinPoint，以此来获取信息
        String name = joinPoint.getSignature().getName();//获取增强方法的名字
        System.out.println("Logger-->返回通知，增强方法为："+name+"，返回结果："+result);

    }

    // @AfterThrowing() 异常
    //目标方法出现异常时，此通知会执行，在切入表达式中增加属性：
    @AfterThrowing(value = "pointCut()",throwing = "exception")
    //增加 Throwable类型参数，参数名必须与切入点表达式保持一致（exception）
    public void aAfterThrowingMethod(JoinPoint joinPoint,Throwable exception){//可以存在参数JoinPoint，以此来获取信息
        String name = joinPoint.getSignature().getName();//获取增强方法的名字
        System.out.println("Logger-->异常通知，增强方法为："+name+"异常信息："+exception);

    }

    // @After() 后置
    @After(value = "execution(* com.haojin.spring.aop.annoaop.CalculatorImpl.*(..))")
    public void afterMethod(JoinPoint joinPoint){//可以存在参数JoinPoint，以此来获取信息
        String name = joinPoint.getSignature().getName();//获取增强方法的名字
        System.out.println("Logger-->后置通知，增强方法为："+name);

    }
    // @Around() 环绕通知，可以通过try-catch-finally,使得增强方法在所有阶段执行（增强方法可设置返回值）
    @Around("execution(public int com.haojin.spring.aop.annoaop.CalculatorImpl.*(..))")
    //可设置 ProceedingJoinPoint属性参数,以此调用增强方法(JoinPoint的子接口，JoinPoint没有调用目标方法的功能)
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();//通过ProceedingJoinPoint参数获取增强方法名
        Object[] args = joinPoint.getArgs();             //获取增强方法参数数组
        String argStr = Arrays.toString(args);           //参数数组转字符串
        Object result = null;

        try{
            System.out.println("环绕通知 == 增强方法执行前执行");

            //通过ProceedingJoinPoint类型参数调用增强方法
            result = joinPoint.proceed();

            System.out.println("环绕通知 = 增强方法返回值之后执行");

        }catch(Throwable throwable){//捕捉Throwable类型异常
            throwable.printStackTrace();
            System.out.println("环绕通知 = 增强方法异常时执行");

        }finally {
            System.out.println("环绕方法 = 增强方法执行完毕执行");

        }

        return result;
    }

    //切面优先级：
    /*
    * 切面的优先级空值切面的 内外嵌套 顺序
    * 外层切面：优先级高
    * 内层切面：优先级低
    *
    * 可使用@Order()注解设置优先级
    * @Order(较小的数) 优先级较高
    * @Order(较大的数) 优先级较低
    * */
}
